//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<CDevice9.h>
///	@path	~/src/graphics/dx9/
///	@date	2007/09/20
///	@desc	DirectX 9 device implementation of IDevice.

#pragma once

#include "xeres.h"

// STL
#include <vector>

#include "graphics/dx9/import_dx9.h"
#include "graphics/IDevice.h"
#include "graphics/animation/frame.h"

#include "lib/system/lock.h"
#include "lib/utilities/string.h"

namespace xeres {

	/*!
		\class	CDevice9
		\brief	Manage DX9 device in this class.
	*/
	class CDevice9 : public IDevice
	{
	public:

		/// \ctor
		CDevice9( void );

		/// \dtor
		virtual ~CDevice9( void );

		DECL_IOBJECT_CLASS_DYNAMIC( CDevice9 , IDevice );

	public:

		/*!
			\brief		Get global Direct 3D 9 interface.
			\return		Interface of D3D 9. 
						Notice this interface's reference count should never be modified.
		*/
		static IDirect3D9 *  d3d9( void );

	//@ interface

		/*!
			\brief		Initialize device from a given window & configuration.
			\return		Result of initialization.
			\param[in]	window		Handle of target window.
			\param[in]	config		Device configuration.
		*/
		virtual bool Initialize( void * window , RefWeak<IData> config );

		/*!
			\brief		Finalize device & its context.
		*/
		virtual void Finalize( void );

		/*!
			\brief		Tick call, give a chance to update device states.
		*/
		virtual void Tick( const Frame& frame );

		/*!
			\brief		Change full screen state.
		*/
		virtual bool ToggleFullScreen( void * window );

		/*!
			\brief		Determine if currently is full screen view.
		*/
		virtual bool IsFullScreen( void );

		/*!
			\brief		Attach device callback.
			\param[in]	callback	Callback instance.
		*/
		virtual void AttachCallback( RefWeak<IDeviceCallback> callback );

		/*!
			\brief		Call to enter device sizing.
		*/
		virtual void EnterSizing( void );

		/*!
			\brief		Call to exit device sizing.
		*/
		virtual void ExitSizing( void );

		/*!
			\brief		Call when window size maybe changed.
		*/
		virtual void OnSize( bool minized );

		/*!
			\brief		Called before start to render scene.
		*/
		virtual bool IsEnableRender( void );

		/*!
			\brief		Call to begin render.
						Return result must be checked,
						while false indicates the device is changing
						that device should not be render.
			\return		Enable render?
		*/
		virtual bool BeginRender( void );

		/*!
			\brief		Call to end render & refresh representation.
		*/
		virtual void EndRender( void );

		/*! 
			\brief		Call to query back buffer size.
		*/
		virtual Pair<int> GetBackBufferSize( void );

	//@ manipulation

		/*!
			\brief		Get raw dx9 interface.
		*/
		IDirect3DDevice9 * GetDevice( void ) const { return m_d3dDevice; }

		/*!
			\brief		Direct access to IDirect3DDevice9 interface.
			\return		Interface of D3D9.
		*/
		IDirect3DDevice9 * operator -> ( void ) const { return m_d3dDevice; }

		/*!
			\brief		Get primary screen resolution.
		*/
		void GetResolution( int& xres , int& yres );

		/*! 
			\brief		Get caps.
		*/
		const D3DCAPS9& GetCaps( void ) const { return m_caps; }

	private:

		// internal methods

		// device settings
		struct _DeviceSettings
		{
			UINT						adapterOrdinal;
			D3DFORMAT					adapterFormat;
			D3DDEVTYPE       			deviceType;
			DWORD						behaviorFlags;
			D3DPRESENT_PARAMETERS		presentParameters;
		};

		// device match type
		enum _MATCH_TYPE
		{
			MATCH_IGNORE_INPUT = 0,  // Use the closest valid value to a default 
			MATCH_PRESERVE_INPUT,    // Use input without change, but may cause no valid device to be found
			MATCH_CLOSEST_TO_INPUT   // Use the closest valid value to the input 
		};

		// device match options
		struct _MatchOptions
		{
			_MATCH_TYPE AdapterOrdinal;
			_MATCH_TYPE DeviceType;
			_MATCH_TYPE Windowed;
			_MATCH_TYPE AdapterFormat;
			_MATCH_TYPE VertexProcessing;
			_MATCH_TYPE Resolution;
			_MATCH_TYPE BackBufferFormat;
			_MATCH_TYPE BackBufferCount;
			_MATCH_TYPE MultiSample;
			_MATCH_TYPE SwapEffect;
			_MATCH_TYPE DepthFormat;
			_MATCH_TYPE StencilFormat;
			_MATCH_TYPE PresentFlags;
			_MATCH_TYPE RefreshRate;
			_MATCH_TYPE PresentInterval;
		};

		// is device windowed
		static inline bool is_windowed( _DeviceSettings * );

		// check for size change
		void CheckForSizeChange( void );

		// select proper device mode
		bool SelectMode( _DeviceSettings * settings );

		// determine if device can be reset
		bool CanDeviceBeReset( _DeviceSettings * oldSettings , _DeviceSettings * newSettings );

		// change device mode
		HRESULT ChangeMode( _DeviceSettings * newSettings );

		// reset environment
		HRESULT ResetEnvironment( void );

		// cleanup environment
		void CleanupEnvironment( bool resetSettings );

		// create environment
		HRESULT CreateEnvironment( void );

		// update back buffer desc
		void UpdateBackBufferDesc( void );

		// find valid device settings
		bool FindValidSettings( _DeviceSettings * prefered , _DeviceSettings * output , _DeviceSettings * optimal ,
			const _MatchOptions * options );

		// build optimal settings
		void BuildOptimalSettings( _DeviceSettings * prefered , _DeviceSettings * optimal ,
			const _MatchOptions * options );

	//@ data

		// operation lock
		ThreadLock				m_lock;

		// data
		HWND                	m_hWnd;
		HMENU					m_hMenu;
		HMONITOR				m_hAdapterMonitor;

		_DeviceSettings			m_currentSettings;

		IDirect3DDevice9 *		m_d3dDevice;
		D3DSURFACE_DESC			m_backBufferSurfaceDesc;
		D3DCAPS9				m_caps;

		// device states
		UINT 					m_fullScreenBackBufferWidthAtModeChange;
        UINT 					m_fullScreenBackBufferHeightAtModeChange;
        UINT 					m_windowBackBufferWidthAtModeChange;
        UINT 					m_windowBackBufferHeightAtModeChange;
        DWORD					m_windowedStyleAtModeChange;
        WINDOWPLACEMENT			m_windowedPlacement;

		bool					m_settingsValid;		// Device settings is valid
		bool					m_ignoreSizeChange;		// Set to make size change call ignored
		bool					m_doNotStoreBufferSize;	// Do not store the buffer size on window size change
		bool					m_noUpdate;				// Device cannot be updated

		bool					m_deviceCreated;
		bool					m_minimized;
		bool					m_deviceLost;			// If true, then the device is lost and needs to be reset
		bool					m_insideDeviceCallback;

		bool					m_enablePerf;
		bool					m_spare0[3];

		// callbacks
		std::vector< Ref<IDeviceCallback> > m_callbacks;
	};

} // namespace xeres
